package io.xxx.sync.core

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import io.xxx.sync.dao.*
import org.quartz.DisallowConcurrentExecution
import org.quartz.Job
import org.quartz.JobExecutionContext
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import java.time.Duration
import java.time.LocalDateTime

/**
 * 生产[SyncSchedule]提供给Synchronizer消费。
 * @see Synchronizer
 */
@Component
@DisallowConcurrentExecution
class ScheduleCalendar : Job {

    @Autowired
    private lateinit var authInfoMapper: AuthInfoMapper

    @Autowired
    private lateinit var propertyMapper: SyncPropertyMapper

    @Autowired
    private lateinit var scheduleMapper: SyncScheduleMapper

    override fun execute(context: JobExecutionContext) {
        val wrapper = QueryWrapper<SyncProperty>()
            .eq("enabled", 1)
            .eq("scheduled", 1)
        val properties: List<SyncProperty> = propertyMapper.selectList(wrapper)
        properties.parallelStream().forEach {
            val authWrapper = QueryWrapper<AuthInfo>()
                .eq("channel", it.channel)
            val authInfos = authInfoMapper.selectList(authWrapper)
            if (authInfos.isEmpty()) {
                saveUncompletedSchedules(it)
            } else {
                authInfos.parallelStream().forEach { authInfo ->
                    saveUncompletedSchedules(it, authInfo)
                }
            }
        }
    }

    private fun saveUncompletedSchedules(property: SyncProperty, authInfo: AuthInfo? = null) {
        val start = System.currentTimeMillis()
        val wrapper = QueryWrapper<SyncSchedule>()
            .eq("property_id", property.id)
            .orderByDesc("end_time")
            .last("limit 1")
        val lastSchedule = scheduleMapper.selectOne(wrapper)
        val startTime = lastSchedule?.endTime ?: property.originTime
        val maxEndTime = LocalDateTime.now().minusSeconds(property.delay.toLong())
        val count = Duration.between(startTime, maxEndTime).seconds / 60

        val schedules = mutableListOf<SyncSchedule>()
        var inserted = false
        var size = 0
        for (i in 0 until count) {
            val newStartTime = startTime.plusMinutes(i)
            val newEndTime = newStartTime.plusMinutes(1)
            val schedule = SyncSchedule(property.id, authInfo?.shopCode, newStartTime, newEndTime)
            schedules.add(schedule)
            if (schedules.size == 1440) {
                scheduleMapper.insertAll(schedules)
                size += schedules.size
                schedules.clear()
                inserted = true
            }
        }
        if (schedules.isNotEmpty()) {
            scheduleMapper.insertAll(schedules)
            size += schedules.size
            schedules.clear()
            inserted = true
        }
        if (log.isDebugEnabled && inserted) {
            val spend = System.currentTimeMillis() - start
            log.debug(
                "Property {} [{} -- {}] added {} schedule records, spend {}.",
                property.id, formatter.format(startTime), formatter.format(maxEndTime),
                size, if (spend < 1000) spend.toString() + "ms" else (spend / 1000).toString() + "s"
            )
        }
    }

    companion object {
        private val log = LoggerFactory.getLogger(ScheduleCalendar::class.java)
    }
}